﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.Serialization;

namespace Log4View.BusinessModel
{
    public class LogEntityQueue : IEnumerable<ILogEntity>, ILogQueue
    {
        private object _syncObject = new object();

        private volatile bool _lockQueue;

        private LinkedList<ILogEntity> _logEntityList;

        private LinkedList<ILogEntity> _takenEntityList;

        private LinkedList<ILogEntity> _addedLogEntityList;

        private LinkedList<ILogEntity> _removedLogEntityList;

        private int _historyLength;

        public LogEntityQueue(int historyLength)
        {
            _logEntityList = new LinkedList<ILogEntity>();
            _takenEntityList = new LinkedList<ILogEntity>();
            _addedLogEntityList = new LinkedList<ILogEntity>();
            _removedLogEntityList = new LinkedList<ILogEntity>();
            _historyLength = historyLength;
            _lockQueue = false;
        }

        public int HistoryLength
        {
            get { return _historyLength; }
        }

        public int Count
        {
            get { return _logEntityList.Count; }
        }

        
        public void Add(ILogEntity logEntity)
        {
            lock (_syncObject)
            {
                if (IsQueueLocked())
                {
                    if (!_addedLogEntityList.Contains(logEntity))
                        _addedLogEntityList.AddLast(logEntity);
                }
                else
                {
                    InternalAdd(logEntity);
                }
            }
        }

        public bool Remove(ILogEntity logEntity)
        {
            lock (_syncObject)
            {
                if (IsQueueLocked())
                {
                    if (_removedLogEntityList.Contains(logEntity))
                        return false;

                    _removedLogEntityList.AddLast(logEntity);
                    
                    return true;
                }
                else
                {
                    return InternalRemove(logEntity);
                }
            }
        }

        private LinkedList<ILogEntity> GetLogEntityList()
        {
            return _logEntityList;
        }

        private void InternalAdd(ILogEntity logEntity)
        {
            if (_logEntityList.Contains(logEntity))
                return;

            if (_logEntityList.Count > _historyLength - 1)
                InternalRemove(_logEntityList.First.Value);


            logEntity.IsOpened = false;
            
            _logEntityList.AddLast(logEntity);
        }

        private bool InternalRemove(ILogEntity logEntity)
        {
            return _logEntityList.Remove(logEntity);
        }

        private bool IsQueueLocked()
        {
            return _lockQueue;
        }

        private void LockQueue()
        {
            if (_lockQueue)
                return;

            lock (_syncObject)
            {
                _addedLogEntityList.Clear();
                _removedLogEntityList.Clear();
                
                _lockQueue = true;
            }
        }

        private void UnlockQueue()
        {
            if (!_lockQueue)
                return;

            lock (_syncObject)
            {
                foreach (var e in _takenEntityList)
                    InternalRemove(e);

                foreach (var entity in _addedLogEntityList)
                    InternalAdd(entity);

                foreach (var entity in _removedLogEntityList)
                    InternalRemove(entity);

                _lockQueue = false;
            }
        }

        #region IEnumerable<ILogEntity> Members

        public IEnumerator<ILogEntity> GetEnumerator()
        {
            lock (_syncObject)
            {
                if (IsQueueLocked())
                    throw new InvalidOperationException("Queue is locked!");

                return new Enumerator(this);
            }
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            lock (_syncObject)
            {
                if (IsQueueLocked())
                    throw new InvalidOperationException("Queue is locked!");

                return new Enumerator(this);
            }
        }

        #endregion

        #region Enumerator

        // Summary:
        //     Enumerates the elements of a LogEntityQueue
        [Serializable]
        public struct Enumerator : IEnumerator<ILogEntity>, IDisposable, IEnumerator
        {
            private LogEntityQueue _logEntityQueue;
            
            private IEnumerator<ILogEntity> _enumerator;

            public Enumerator(LogEntityQueue logEntityQueue)
            {
                _logEntityQueue = logEntityQueue;
                _enumerator = _logEntityQueue.GetLogEntityList().GetEnumerator();
            }

            public ILogEntity Current
            {
                get { return _enumerator.Current; }
            }
            
            public void Dispose()
            {
                if (_enumerator == null)
                    return;
                
                if (_logEntityQueue.IsQueueLocked())
                    _logEntityQueue.UnlockQueue();

                _enumerator.Dispose();
                _enumerator = null;
            }

            public bool MoveNext()
            {
                if (!_enumerator.MoveNext())
                {
                    //_logEntityQueue.UnlockQueue();
                    return false;
                }
                
                _logEntityQueue.LockQueue();
                _logEntityQueue._takenEntityList.AddLast(Current);

                return true;
            }

            public void Reset()
            {
                _logEntityQueue.LockQueue();
                _enumerator.Reset();
            }

            #region IEnumerator Members

            object IEnumerator.Current
            {
                get { return Current; }
            }

            #endregion
        }

        #endregion
    }
}
